package bs.org.suite.tool;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JTextArea;

import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyException;
import net.contentobjects.jnotify.JNotifyListener;

/**
 * 监控文件(夹)增删改变化.
 */
public class JNotifyUtils {

	/**
	 * 输出到控制台.
	 */
	public JNotifyUtils() {
		if ((this.printMode & PRINT_MODE_CONSOLE) != PRINT_MODE_CONSOLE) {
			this.printMode += PRINT_MODE_CONSOLE;
		}
	}

	/**
	 * 输出到Swing JTextArea.
	 */
	public JNotifyUtils(JTextArea textArea) {
		addPrintModeJTextArea(textArea);
	}

	/**
	 * 输出到文件.
	 */
	public JNotifyUtils(String filePath) {
		addPrintModeFile(filePath);
	}

	/**
	 * 输出到Swing JTextArea、文件.
	 */
	public JNotifyUtils(JTextArea textArea, String filePath) {
		addPrintModeJTextArea(textArea);
		addPrintModeFile(filePath);
	}

	/**
	 * 输出到Swing JTextArea.
	 */
	private void addPrintModeJTextArea(JTextArea textArea) {
		this.textArea = textArea;
		if ((this.printMode & PRINT_MODE_SWING_JTEXTAREA) != PRINT_MODE_SWING_JTEXTAREA) {
			this.printMode += PRINT_MODE_SWING_JTEXTAREA;
		}
	}

	/**
	 * 输出到文件.
	 */
	private void addPrintModeFile(String filePath) {
		File file = new File(filePath);
		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			txtOut = new FileOutputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		if ((this.printMode & PRINT_MODE_TXT) != PRINT_MODE_TXT) {
			this.printMode += PRINT_MODE_TXT;
		}
	}

	/**
	 * 输出到控制台.
	 */
	public static final int PRINT_MODE_CONSOLE = 0x1;
	/**
	 * 输出到Txt文本.
	 */
	public static final int PRINT_MODE_TXT = 0x2;
	/**
	 * 输出到Swing JTextArea.
	 */
	public static final int PRINT_MODE_SWING_JTEXTAREA = 0x4;
	/**
	 * 输出到控制台、Txt文本、Swing TextArea.
	 */
	public static final int PRINT_MODE_ALL = PRINT_MODE_CONSOLE | PRINT_MODE_TXT | PRINT_MODE_SWING_JTEXTAREA;

	/**
	 * 输出方式, 默认控制台输出.
	 */
	private int printMode;

	/**
	 * 输出到Txt文本的输出流.
	 */
	private OutputStream txtOut;

	/**
	 * 输出到JTextArea控件.
	 */
	private JTextArea textArea;

	/**
	 * 监控日志输出文件/文件夹名参数.
	 */
	private JNotifyLogOutputParams notifyParams;

	/**
	 * Main入口.
	 * 
	 * <pre>
	 * 接受一个或两个参数; 
	 * 当传入一个参数, 参数是"监控路径", 监控日志输出到控制台;
	 * 当传入两个参数, 参数依次是"监控路径", "监控日志绝对路径", 监控日志输出到文件; 
	 * 不接受其它情况.
	 * </pre>
	 */
	public static void main(String[] args) throws IOException, InterruptedException {
		int len = args.length;
		JNotifyUtils notify;
		if (len == 1) {
			notify = new JNotifyUtils();
		} else if (len == 2) {
			notify = new JNotifyUtils(args[1]);
		} else {
			throw new IllegalArgumentException(
					"Parameters error: params number must 1 or 2, First param is monitor absolute path and Second is monitor log absolute path.");
		}
		int watchId = notify.startMonitor(args[0], null);
		// Sleep a little, the application will exit if you don't (watching is asynchronous), depending on your application, this may not be required.
		// Default sleep 100 days.
		Thread.sleep(8640000);
		notify.stopMonitor(watchId);
	}

	/**
	 * 开始监控.
	 */
	public int startMonitor(String notifyPath, Map<String, Object> paramsMap) throws IOException {
		if (!new File(notifyPath).exists()) {
			throw new IOException("The absolute path to monitor is not exist.");
		}
		notifyParams = new JNotifyLogOutputParams(paramsMap);
		logPrint(getCurTime() + "  Start Monitor ... ... ... ... ... ... ... ...");

		// watch mask, specify events you care about, or JNotify.FILE_ANY for all events.
		int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED;
		// watch subtree
		boolean watchSubtree = true;
		return JNotify.addWatch(notifyPath, mask, watchSubtree, new Listener());
	}

	/**
	 * 停止监控.
	 */
	public boolean stopMonitor(int watchId) throws JNotifyException {
		logPrint(getCurTime() + "  Stop Monitor ... ... ... ... ... ... ... ...");
		if (txtOut != null) {
			try {
				txtOut.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			txtOut = null;
		}
		return JNotify.removeWatch(watchId);
	}

	/**
	 * 当前时间.
	 */
	private String getCurTime() {
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		return sf.format(new Date());
	}

	/**
	 * 记录创建文件(夹)日志.
	 */
	private void logFileCreated(int wd, String rootPath, String name) {
		if (ifLog(rootPath, name)) {
			logPrint(getCurTime() + "  Create  " + name);
		}
	}

	/**
	 * 记录重命名文件(夹)日志.
	 */
	private void logFileRenamed(int wd, String rootPath, String oldName, String newName) {
		if (ifLog(rootPath, oldName) || ifLog(rootPath, newName)) {
			logPrint(getCurTime() + "  Rename  " + oldName + " -> " + newName);
		}
	}

	/**
	 * 记录修改文件(夹)日志.
	 */
	private void logFileModified(int wd, String rootPath, String name) {
		if (ifLog(rootPath, name)) {
			logPrint(getCurTime() + "  Modify " + name);
		}
	}

	/**
	 * 记录删除文件(夹)日志.
	 */
	private void logFileDeleted(int wd, String rootPath, String name) {
		if (ifLog(rootPath, name)) {
			logPrint(getCurTime() + "  Delete  " + name);
		}
	}

	/**
	 * 根据输出方式输出.
	 */
	private void logPrint(String text) {
		text = text + "\n";
		if ((printMode & PRINT_MODE_CONSOLE) == PRINT_MODE_CONSOLE) {
			System.out.print(text);
		}
		if ((printMode & PRINT_MODE_TXT) == PRINT_MODE_TXT) {
			try {
				txtOut.write(text.getBytes());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if ((printMode & PRINT_MODE_SWING_JTEXTAREA) == PRINT_MODE_SWING_JTEXTAREA) {
			textArea.append(text);
		}
	}

	private static String fileSeparator = System.getProperty("file.separator");

	/**
	 * 是否输出.
	 */
	private boolean ifLog(String rootPath, String name) {
		File file = new File(rootPath + fileSeparator + name);
		String fileName = "";
		String folder = "";
		if (!file.isDirectory()) {
			int lIndex = name.lastIndexOf(fileSeparator);
			fileName = name.substring(lIndex + 1);
			if (lIndex != -1) {
				folder = name.substring(0, lIndex);
			}
		} else {
			folder = name;
		}
		if (fileName.length() != 0) {
			boolean fileLog = ifMatchText(fileName, notifyParams.fileNameCsText, notifyParams.fileNameNCsText,
					notifyParams.fileNameSRegex, notifyParams.fileNameCsPattern, notifyParams.fileNameNCsPattern);
			if (fileLog) {
				return true;
			}
		}
		if (folder.length() != 0) {
			return ifMatchText(folder, notifyParams.folderPathCsText, notifyParams.folderPathNCsText,
					notifyParams.folderPathSRegex, notifyParams.folderPathCsPattern, notifyParams.folderPathNCsPattern);
		}
		return false;
	}

	/**
	* 判断文件(夹)名是否满足匹配.
	*/
	public static boolean ifMatchText(String fileName, String csText, String ncsText, boolean sRegex,
			Pattern csPattern, Pattern ncsPattern) {
		boolean match = true;
		String lFileName = fileName.toLowerCase();
		String lcsText = csText.toLowerCase();
		String lncsText = ncsText.toLowerCase();
		if (sRegex) {
			if (csText.length() != 0) {
				Matcher m = csPattern.matcher(fileName);
				match = m.find();
			}
			if (match && ncsText.length() != 0) {
				Matcher m = ncsPattern.matcher(fileName);
				match = !m.find();
			}
		} else {
			if (csText.length() != 0) {
				match = lFileName.contains(lcsText);
			}
			if (match && ncsText.length() != 0) {
				match = !lFileName.contains(lncsText);
			}
		}
		return match;
	}

	class Listener implements JNotifyListener {
		public void fileCreated(int wd, String rootPath, String name) {
			logFileCreated(wd, rootPath, name);
		}

		public void fileRenamed(int wd, String rootPath, String oldName, String newName) {
			logFileRenamed(wd, rootPath, oldName, newName);
		}

		public void fileModified(int wd, String rootPath, String name) {
			logFileModified(wd, rootPath, name);
		}

		public void fileDeleted(int wd, String rootPath, String name) {
			logFileDeleted(wd, rootPath, name);
		}
	}

	public JTextArea getTextArea() {
		return textArea;
	}

	public void setTextArea(JTextArea textArea) {
		this.textArea = textArea;
	}

}

class JNotifyLogOutputParams {
	public String fileNameCsText = "";
	public String fileNameNCsText = "";
	public boolean fileNameSRegex = false;
	public String folderPathCsText = "";
	public String folderPathNCsText = "";
	public boolean folderPathSRegex = false;

	public Pattern fileNameCsPattern;
	public Pattern fileNameNCsPattern;
	public Pattern folderPathCsPattern;
	public Pattern folderPathNCsPattern;

	public JNotifyLogOutputParams(Map<String, Object> paramsMap) {
		if (paramsMap == null) {
			return;
		}
		fileNameCsText = (String) paramsMap.get("fileNameContainsText");
		fileNameNCsText = (String) paramsMap.get("fileNameNotContainsText");
		fileNameSRegex = (Boolean) paramsMap.get("fileNameSupportRegex");
		folderPathCsText = (String) paramsMap.get("folderPathContainsText");
		folderPathNCsText = (String) paramsMap.get("folderPathNotContainsText");
		folderPathSRegex = (Boolean) paramsMap.get("folderPathSupportRegex");

		if (fileNameSRegex) {
			fileNameCsPattern = Pattern.compile(fileNameCsText, Pattern.CASE_INSENSITIVE);
			fileNameNCsPattern = Pattern.compile(fileNameNCsText, Pattern.CASE_INSENSITIVE);
		}
		if (folderPathSRegex) {
			folderPathCsPattern = Pattern.compile(folderPathCsText, Pattern.CASE_INSENSITIVE);
			folderPathNCsPattern = Pattern.compile(folderPathNCsText, Pattern.CASE_INSENSITIVE);
		}
	}
}
